﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace PoliAsis.Domain.Common
{

    public sealed class QueryMap
    {
        private static volatile QueryMap instance;
        private static object locker = new object();
        private string config_file_path;
        private string CS_KEY_NAME = "configuration_map_path";

        Dictionary<Type, List<Tuple<string, string>>> property_map;
        Dictionary<Type, List<string>> key_map;
        Dictionary<Type, string> table_map;

        private QueryMap()
        {
            property_map = new Dictionary<Type, List<Tuple<string, string>>>();
            key_map = new Dictionary<Type, List<string>>();
            table_map = new Dictionary<Type, string>();
            config_file_path = ConfigurationManager.AppSettings[CS_KEY_NAME];
            config_file_path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, config_file_path);
            LoadMap();
        }

        public static QueryMap Map
        {
            get
            {
                if (instance == null)
                {
                    lock (locker)
                    {
                        if (instance == null)
                            instance = new QueryMap();
                    }
                }
                return instance;
            }
        }

        private void LoadMap()
        {
            //string obj_table_name = table_type.Name.ToLower();
            String[] list = File.ReadAllLines(config_file_path);
            //PropertyInfo[] prop_list = table_type.GetProperties();
            string table_type = String.Empty;
            string table_name = String.Empty;
            List<Tuple<string, string>> plist = new List<Tuple<string, string>>();
            Type type = null;
            PropertyInfo[] property_info_list = null;
            for (int i = 0; i < list.Length; i++)
            {


                //empty line: go next line
                if (String.IsNullOrEmpty(list[i])) continue;

                //end of config file, add the last propert map list, and stop
                if (list[i].StartsWith("--endconfig"))
                {
                    property_map.Add(type, plist);
                    break;
                }
                //comments: go next line
                if (list[i].StartsWith("--")) continue;

                //detected new table
                if (list[i].StartsWith(@"//"))
                {
                    if (plist.Count > 0)
                    {
                        property_map.Add(type, plist);
                        plist = new List<Tuple<string, string>>();
                    }
                    table_type = list[i].Split(':')[0].TrimStart('/');
                    table_name = list[i].Split(':')[1];
                    type = Type.GetType(table_type, true, true);
                    table_map.Add(type, table_name);

                    //set the key_list
                    TableKey tk = type.GetCustomAttributes(typeof(TableKey), false).FirstOrDefault() as TableKey;
                    if (tk != null)
                    {
                        key_map.Add(type, tk.key_list);
                    }
                    else
                    {
                        key_map.Add(type, new List<string>() { "id" });
                    }

                    property_info_list = type.GetProperties();
                    continue;
                }

                //map the properties for the type: table_name                             
                string prop_name = list[i].Split(':')[0];
                string column_name = list[i].Split(':')[1];
                bool found_prop_map = false;
                //map the properties from config file
                foreach (PropertyInfo pi in property_info_list)
                {
                    if (pi.Name.ToLower().Trim().Equals(prop_name.ToLower().Trim()))
                    {
                        Tuple<string, string> t = new Tuple<string, string>(pi.Name, column_name);
                        plist.Add(t);
                        found_prop_map = true;
                        break;
                    }
                }

                //the property is not in the map
                if (!found_prop_map)
                {
                    throw new Exception("Table/Class: " + table_name + ", Column/Property: " + prop_name + " not in the config map. Add the property in the config map.");
                }
            }

        }

        public string GetTableName(Type type)
        {
            return table_map[type];
        }
        public string GetColumnName(Type type, string property_name)
        {
            List<Tuple<string, string>> l = property_map[type];
            return (from it in l
                    where it.Item1 == property_name
                    select it.Item2).FirstOrDefault();
        }
        public List<string> GetTableColumns(Type type)
        {
            return (from mp in property_map[type]
                    select mp.Item2).ToList();
        }
        public List<string> GetClassProperties(Type type)
        {
            return (from mp in property_map[type]
                    select mp.Item1).ToList();
        }
        public string GetTableColumnsCsep(Type type)
        {
            return string.Join(",", GetTableColumns(type));
        }
        public List<string> GetTableKeys(Type type)
        {
            return key_map[type];
        }
        public string GetTableKeysCsep(Type type)
        {
            return string.Join(",", GetTableKeys(type));
        }
        public string GetDataTableType(Type type)
        {
            return GetTableName(type) + "_type";
        }
    }


}
